package org.reborn.model.bound

import org.reborn.math.{VectorF,Line3F,Plane3F}

class AxisAlignedBoundingBox(val min:VectorF, val max:VectorF) extends ClosedPolygonalBound {
  require(min.length == 3)
  require(max.length == 3)
  
  lazy val points:Vector[VectorF] = calcPoints
  lazy val edges:Vector[Line3F] = calcLines
  lazy val planes:Vector[Plane3F] = calcPlanes
  lazy val center:VectorF = (max-min) * .5f + min
  
  val aabb = this
  lazy val boundingSphere:BoundingSphere = calcBoundingSphere
  
  private def calcPoints:Vector[VectorF] = {
	Vector[VectorF](
	  min,
	  VectorF(max(0),min(1),min(2)),
	  VectorF(max(0),max(1),min(2)),
	  VectorF(min(0),max(1),min(2)),
	  VectorF(min(0),min(1),max(2)),
	  VectorF(max(0),min(1),max(2)),
	  max,
	  VectorF(min(0),max(1),max(2))
	)
  }
  
  private def calcLines:Vector[Line3F] = {
	Vector[Line3F](
	  new Line3F(points(0),points(4)),
	  new Line3F(points(1),points(5)),
	  new Line3F(points(2),points(6)),
	  new Line3F(points(3),points(7)),
	  new Line3F(points(0),points(1)),
	  new Line3F(points(1),points(2)),
	  new Line3F(points(2),points(3)),
	  new Line3F(points(3),points(0)),
	  new Line3F(points(4),points(5)),
	  new Line3F(points(5),points(6)),
	  new Line3F(points(6),points(7)),
	  new Line3F(points(7),points(4))
	)
  }
  
  private def calcPlanes = {
	Vector[Plane3F](
	  new Plane3F(min,VectorF(-1f,0f,0f)),		//the left plane
	  new Plane3F(min,VectorF(0f,-1f,0f)),		//the bottom plane
	  new Plane3F(min,VectorF(0f,0f,-1f)),		//the back plane
	  new Plane3F(max,VectorF(1f,0f,0f)),		//the right plane
	  new Plane3F(max,VectorF(0f,1f,0f)),		//the top plane
	  new Plane3F(max,VectorF(0f,0f,1f))		//the front plane
	)
  }
  
  private def calcBoundingSphere:BoundingSphere = {
	new BoundingSphere(center,(max-min).magnitude * .5f)
  }
  
  override def containsPoint(point:VectorF):Boolean = {
	min(0) <= point(0) && point(0) <= max(0) &&
	min(1) <= point(1) && point(1) <= max(1) &&
	min(2) <= point(2) && point(2) <= max(2)
  }
}

object AxisAlignedBoundingBox{
  def apply(verts:Array[Float]):AxisAlignedBoundingBox = {
	val min = Array[Float](Float.MaxValue,Float.MaxValue,Float.MaxValue)
	val max = Array[Float](Float.MinValue,Float.MinValue,Float.MinValue)
	
	for(i <- 0 until verts.size){
	  val ind = i%3
	  val vertPart = verts(i)
	  if(min(ind) > vertPart)
	 	  min(ind) = vertPart
	  if(max(ind) < vertPart)
	 	  max(ind) = vertPart
	}
	
    new AxisAlignedBoundingBox(VectorF(min),VectorF(max))
  }
}